#include "path.h"

Path::Path()
{
}

void Path::draw(QPaintDevice* device, qreal scale)
{
    QPainter painter;
    QPen pen;
    QBrush brush;

    pen.setColor(this->strokeColor);
    pen.setWidth(this->strokeWidth);
    pen.setJoinStyle(Qt::MiterJoin);

    brush.setColor(this->fillColor);
    brush.setStyle(Qt::SolidPattern);

    painter.begin(device);

    painter.scale(scale, scale);
    painter.setPen(pen);
    painter.setBrush(brush);

    QPainterPath path;
    foreach(Segment p, this->segments)
    {
        switch (p.type)
        {
        case Point_LineTo:
            path.lineTo(p.point);
            break;
        case Point_MoveTo:
            path.moveTo(p.point);
            break;
        }
    }
    painter.drawPath(path);

    painter.end();
}

QDomElement Path::saveToNode(QDomDocument &domDoc)
{
    QDomElement domElement = domDoc.createElement("path");//Создание узла элемента
    //добавление свойств

    QMap<QString,QString> styleMap;

    styleMap.insert("fill",getHexColor(fillColor));
    styleMap.insert("stroke",getHexColor(strokeColor));
    styleMap.insert("fill-opacity",QString::number(fillColor.alphaF()));
    styleMap.insert("stroke-opacity",QString::number(strokeColor.alphaF()));
    styleMap.insert("stroke-width",QString::number(strokeWidth));

    domElement.setAttribute("style",constructStyle(styleMap));

    QString pathLine;
    //Цикл по элементам вектора
    foreach (Segment p, this->segments)
    {
        switch (p.type)
        {
            case Point_LineTo:
                pathLine += "L";
                break;

            case Point_MoveTo:
                pathLine += "M";
                break;
        }
        pathLine += QString::number(p.point.x()) + "," + QString::number(p.point.y());
    }
    domElement.setAttribute("d",pathLine);

    return domElement;
}

void Path::loadFromNode(const QDomElement &node)
{
    // Читаем атрибут style
    QString style = node.attribute("style","");
    QMap<QString, QString> stylemap = parseStyle(style);

    // Цвет заполнения
    if (stylemap.contains("fill"))
    {
        QString fillValue = stylemap.value("fill");
        if(fillValue == "none")
            fillColor = QColor(0,0,0,0);
        else
            fillColor = QColor(fillValue);
    }

    // Цвет линии
    if (stylemap.contains("stroke"))
    {
        QString strokeValue = stylemap.value("stroke");
        if(strokeValue == "none")
            strokeColor = QColor(0,0,0,0);
        else
            strokeColor = QColor(strokeValue);
    }

    // Прозрачность заполнения
    if (stylemap.contains("fill-opacity"))
        fillColor.setAlphaF(stylemap.value("fill-opacity").toFloat());

    // Прозрачность линии
    if (stylemap.contains("stroke-opacity"))
        strokeColor.setAlphaF(stylemap.value("stroke-opacity").toFloat());

    // Толщина линии
    if (stylemap.contains("stroke-width"))
    {
        QString strokeWidthValue = stylemap.value("stroke-width");
        strokeWidth = strokeWidthValue.toInt();
    }

    QString path = node.attribute("d");    
    loadSegments(path);
}

Path::CharState Path::getStateByChar(QChar chr)
{
    char asciiChar = chr.toAscii();
    if ((asciiChar == 'L') || (asciiChar == 'M'))
        return CharState_Char;
    if (asciiChar == ',')
        return CharState_Delimiter;
    if (asciiChar == '-')
        return CharState_Minus;
    if (chr.isDigit())
        return CharState_Number;
    return CharState_Illegal;
}

bool Path::isNumber(QString str)
{
    bool ok;
    str.toInt(&ok);
    return ok;
}

Path::Segment Path::flushSegment(QString x, QString y, char pointType)
{
    Segment segment;
    segment.point.setX(x.toInt());
    segment.point.setY(y.toInt());
    segment.type = pointType == 'L' ? Point_LineTo : Point_MoveTo;
    return segment;
}

void Path::loadSegments(QString path)
{
    segments.clear();

    if (path.size() > 0)
    {
        QString xCoord;
        QString yCoord;
        char pointType;

        LoaderState state = LoaderState_Init;
        int i = 0;

        while ((i < path.size()) && (state != LoaderState_Illegal))
        {
            CharState charState = getStateByChar(path.at(i));
            switch (charState)
            {
            //Направляющая буква
            case CharState_Char:
                if ((state == LoaderState_YCoord) && isNumber(yCoord))
                {
                    segments.append(flushSegment(xCoord,yCoord,pointType));

                    xCoord.clear();
                    yCoord.clear();
                    pointType = path.at(i).toAscii();
                    state = LoaderState_Char;
                }
                else
                    if (state == LoaderState_Init)
                    {
                        pointType = path.at(i).toAscii();
                        state = LoaderState_Char;
                    }
                    else
                        state = LoaderState_Illegal;
                break;

            // Запятая - разделитель
            case CharState_Delimiter:
                if ((state != LoaderState_XCoord) || !isNumber(xCoord))
                    state = LoaderState_Illegal;
                else
                    state = LoaderState_Delimiter;
                break;

            // Знак минус
            case CharState_Minus:
                if ((state == LoaderState_XCoord) || (state == LoaderState_YCoord))
                   state = LoaderState_Illegal;
                else
                {
                    if (state == LoaderState_Char)
                    {
                        state = LoaderState_XCoord;
                        xCoord = "-";
                    }
                    else
                        if (state == LoaderState_Delimiter)
                        {
                            state = LoaderState_YCoord;
                            yCoord = "-";
                        }
                }
                break;

            // Цифра
            case CharState_Number:
                if ((state == LoaderState_XCoord) || (state == LoaderState_Char))
                {
                    xCoord.append(path.at(i));
                    state = LoaderState_XCoord;
                }
                else if ((state == LoaderState_YCoord) || (state == LoaderState_Delimiter))
                {
                    yCoord.append(path.at(i));
                    state = LoaderState_YCoord;
                }
                break;

            // Неверное состояние
            case CharState_Illegal:
                state = LoaderState_Illegal;
                break;
            }
            i++;
        }

        if ((state == LoaderState_YCoord) && isNumber(yCoord))
            segments.append(flushSegment(xCoord,yCoord,pointType));
        else
        {
#warning Throw exception
        }
    }
}
